﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved All Rights Reserved All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.Text.RegularExpressions;
using FinPlusInterfaces;

namespace FinPlusAssembler
{
    public static class StringExtension
    {
        //enum 
        public static bool EnumIsDefined<T>(this string name)
        {
            if (Enum.IsDefined(typeof(T), name))
                return true;

            return false;
        }

        public static T EnumParse<T>(this string name, T defaultValue)
        {
            if (!Enum.IsDefined(typeof(T), name))
                return defaultValue;

            return (T)Enum.Parse(typeof(T), name);
        }

        public static T EnumParse<T>(this string name)
        {
            if (!Enum.IsDefined(typeof(T), name))
                throw new InvalidOperationException(string.Format("{} not defined", name));

            return (T)Enum.Parse(typeof(T), name);
        }

        //array 
        public static object[] ToArray1d(this string value, char delimiter = '%', string brackets = null, string remove = @"\t|\n|\r| ", string replace = "")
        {
            if (string.IsNullOrEmpty(value))
                return null;

            if (brackets != null)
                value = value.ReplaceClean(remove, replace).Trim(brackets.ToCharArray());

            var array = value.Split(new char[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
            return array;
        }

        public static object ToArray2d(this string value, char delimiter, string brackets, string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                var b = brackets.ToCharArray();
                if (b.Length != 2)
                    throw new FormatException("incorect bracket format should be 2 char string");

                var str0 = value.ReplaceClean(remove, replace);
                var str1 = str0.Replace(string.Format("{0}{1}{2}", b[1], delimiter, b[0]), ";");
                var str2 = str1.Replace(b[0].ToString(), string.Empty).Replace(b[1].ToString(), string.Empty);

                return ToArray2d(str2, '¬', delimiter);
            }
            catch (Exception e)
            {
                return new string[,] { { "Error" }, { e.Message } };
            }
        }

        public static object ToArray2d(this string value, char rowDelimiter = '%', char colDelimiter = '$')
        {
            try
            {
                if (string.IsNullOrEmpty(value))
                    return new string[,] { { } };

                string[] rows = value.Split(rowDelimiter);
                string[,] array = null;

                for (int i = 0; i < rows.GetLength(0); i++)
                {
                    string[] cols = rows[i].Split(colDelimiter);
                    if (array == null)
                        array = new string[rows.GetLength(0), cols.GetLength(0)];

                    for (int j = 0; j < cols.GetLength(0); j++)
                        array[i, j] = cols[j];
                }
                return array;
            }
            catch (Exception e)
            {
                return new string[,] { { "Error" }, { e.Message } };
            }
        }

        public static string[][] ToJaggedArray(this string value, char rowDelimiter = '\n', char colDelimiter = ',')
        {
            try
            {
                return value.Split(rowDelimiter).Select(i => i.Split(colDelimiter).ToArray()).ToArray();
            }
            catch
            {
                return null;
            }
        }

        public static string[] Split(this string value, char delimiter = ',', string remove = @"\t|\n|\r| ", string replace = "")
        {
            return value.ReplaceClean(remove, replace).Split(delimiter);
        }

        //dict 
        public static IDictionary<string, string> ToDict(this string value, bool isKeyFirst = true, char delimiter1 = ';', char delimiter2 = ':', string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                if (string.IsNullOrEmpty(value) || value.Equals("NA"))
                    return new Dictionary<string, string>();

                if (isKeyFirst)
                    return value.TrimEnd(delimiter1).Split(delimiter1, remove, replace).ToDictionary(f => f.Split(delimiter2)[0], f => f.Split(delimiter2)[1]);

                return value.Split(delimiter1, remove, replace).ToDictionary(f => f.Split(delimiter2)[1], f => f.Split(delimiter2)[0]);
            }
            catch
            {
                return null;
            }
        }

        public static IDictionary<string, string> ToDict(this string value, char delimiter = '%', string brackets = "{}", string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                if (string.IsNullOrEmpty(value))
                    return null;

                if (!string.IsNullOrEmpty(remove))
                    value = value.Replace(remove, replace);

                var b = brackets.ToCharArray();
                if (b.Length != 2)
                    throw new FormatException("incorect bracket format should be 2 char string");

                var res = new Dictionary<string, string>();
                var array = value.Replace(string.Format("{0}{1}{2}", b[1], delimiter, b[0]), "¬").Split('¬');

                foreach (var val in array)
                {
                    var kvp = val.Split(delimiter);
                    res.Add(kvp[0].Trim(b), kvp[1].Trim(b));
                }

                return res;
            }
            catch (Exception e)
            {
                throw new FormatException(string.Format("unable to parse {0}", value), e);
            }
        }

        public static IDictionary<string, string[]> ToDictOfArrays(this string value, char delimiter = '%', string brackets = "{}", string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                if (string.IsNullOrEmpty(value) || value.Equals(brackets))
                    return null;

                if (!string.IsNullOrEmpty(remove))
                    value = value.ReplaceClean(remove, replace);

                var b = brackets.ToCharArray();
                if (b.Length != 2)
                    throw new FormatException("incorrect bracket format should be 2 char string");

                var res = new Dictionary<string, string[]>();
                var array = value.Replace(string.Format("{0}{1}{2}", b[1], delimiter, b[0]), "¬").Split('¬');

                foreach (var val in array)
                {
                    var kvp = val.Replace(string.Format("{0}{1}", delimiter, b[0]), "¬").Split('¬');
                    res.Add(kvp[0].Trim(b), kvp[1].Trim(b).Split(delimiter));
                }

                return res;
            }
            catch (Exception e)
            {
                throw new FormatException(string.Format("unable to parse {0}", value), e);
            }
        }

        //list 
        public static IList<string[]> ToListOfArrays(this string value, char delimiter = '%', string brackets = "{}", string remove = @"\t|\n|\r| ", string replace = "")
        {
            try
            {
                if (string.IsNullOrEmpty(value))
                    return null;

                if (!string.IsNullOrEmpty(remove))
                    value = value.ReplaceClean(remove, replace);

                var b = brackets.ToCharArray();
                if (b.Length != 2)
                    throw new FormatException("incorect bracket format should be 2 char string");

                var res = new List<string[]>();
                var array = value.Trim(b).Replace(string.Format("{0}{1}{2}", b[1], delimiter, b[0]), "¬").Split('¬');

                foreach (var vals in array)
                {
                    var val = vals.Split(delimiter);
                    res.Add(val);
                }

                return res;
            }
            catch (Exception e)
            {
                throw new FormatException(string.Format("unable to parse {0}", value), e);
            }
        }

        //primative 
        public static object IntParse(this string value)
        {
            int res;
            if (int.TryParse(value, out res))
                return res;

            return value;
        }

        public static int IntParse(this string value, int defaultValue)
        {
            if (value == null)
                return defaultValue;

            int.TryParse(value, out defaultValue);

            return defaultValue;
        }

        public static double DoubleParse(this string value, double defaultValue)
        {
            if (value == null)
                return defaultValue;

            double.TryParse(value, out defaultValue);

            return defaultValue;
        }

        //date time 
        public static DateTime ParseDateTime(this string value)
        {
            var l = value.ToLower();
            if (l.Equals("today") || l.Equals("t"))
                return DateTime.Now.Date;

            if (l.Equals("yesterday") || l.Equals("t-1"))
                return DateTime.Now.Date.NextWorkDay(-1);

            DateTime res1 = DateTime.MaxValue;

            if (DateTime.TryParse(value, out res1))
                return res1;

            double res2;
            if (double.TryParse(value, out res2))
                return DateTime.FromOADate(res2);

            return res1;
        }

        public static object DateTimeParse(this string value)
        {
            DateTime res;
            if (DateTime.TryParse(value, out res))
                return res;

            double res2;
            if (double.TryParse(value, out res2))
                return res2 < 1 ? DateTime.MinValue : DateTime.FromOADate(res2);

            return value;
        }

        //replace
        public static string ReplaceFirst(this string value, string find, string replace)
        {
            int Place = value.IndexOf(find);
            string result = value.Remove(Place, find.Length).Insert(Place, replace);
            return result;
        }

        public static string ReplaceLast(this string value, string find, string replace)
        {
            int Place = value.LastIndexOf(find);
            string result = value.Remove(Place, find.Length).Insert(Place, replace);
            return result;
        }

        public static string ReplaceClean(this string value, string remove = @"\t|\n|\r| ", string replace = "") 
        {      
            return Regex.Replace(value, remove, replace);
        }

        public static string RemoveNumeric(this string value)
        {
            return Regex.Replace(value, "[0-9]", "");
        }

        public static string RemoveAlpha(this string value)
        {
            return Regex.Replace(value, "[^.0-9]", "");
        }

        public static string Replace(this string value, IDictionary<string, string> replace)
        {
            if (replace != null && value.StartsWith("[") && value.EndsWith("]") && replace.ContainsKey(value))
                value = replace[value];
            else if (replace != null && replace.Count > 0)
                foreach (var val in replace)
                    if (val.Key.StartsWith("[") && val.Key.EndsWith("]") && value.Contains(val.Key))
                        value = value.ReplaceLast(val.Key, val.Value);

            return value;
        }

        public static string SplitCamelCase(this string value)
        {
            return Regex.Replace(Regex.Replace(value, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2");
        }

        //get assembly
        public static Assembly GetAssembly(this string path, string name)
        {
            try
            {
                return Assembly.LoadFile(string.Format("{0}{1}.dll", path, name));
            }
            catch
            {
                return null;
            }
        }

        //config
        public static Env ConfigEnv(this string path)
        {
            var doc = new XmlDocument();
            doc.Load(path);
            Env environment;
            var env = doc.SelectSingleNode("configuration/environment").Attributes["name"].Value;
            var config = doc.SelectNodes("configuration/" + env);
            if (!Enum.TryParse(env, out environment))
                throw new FormatException("!config environment " + env + " invalid should be dev, uat or prod");
            return environment;
        }

        public static Config ConfigXml(this string path, out Env environment)
        {
            try
            {
                var doc = new XmlDocument();

                doc.Load(path);
                var res = new Config();

                var env = doc.SelectSingleNode("configuration/environment").Attributes["name"].Value;
                var config = doc.SelectNodes("configuration/" + env);
                if (!Enum.TryParse(env, out environment))
                    throw new FormatException("!config environment " + env + " invalid should be dev, uat or prod");

                foreach (XmlElement settings in config)
                {
                    foreach (XmlElement setting in settings)
                    {
                        var d = res[setting.Name] = new Dictionary<string, string>();
                        foreach (XmlElement value in setting)
                            d[value.Attributes["name"].Value] = value.Attributes["value"].Value;
                    }
                }

                return res;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

        public static bool RegxTryGet(this string input, string pattern, out string value)
        {
            value = string.Empty;
            var regx = Regex.Match(input, pattern);
            if (!regx.Groups["result"].Success)
                return false;

            value = regx.Groups["result"].Value.ToString();

            return true;
        }   
    }

    public static class Guids
    {
        public static string New()
        {
            return System.Guid.NewGuid().ToString().Replace("$", "£").Replace("%", "^");
        }
    }
}
